﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IronPhpConsole {
    using System.Collections;
    using System.IO;
    using System.Linq.Expressions;

    using IronPhp;

    using Irony.Interpreter.Ast;
    using Irony.Parsing;

    class Program {
        static void Main(string[] args) {
            if (args.Length <= 0 || (args[0].ToLower().CompareTo("-t") != 0 && !File.Exists(args[0]))) {
                Console.WriteLine("Please provide a php file to execute. If you would like to run a test on the file use the '-t' flag.");
                return;
            }

            if (args[0].ToLower().CompareTo("-t") == 0) {
                if (args.Length < 2) {
                    Console.WriteLine("Please provide the php executable for the second argment (when using the '-t' flag).");
                    Console.WriteLine("Optionally, provide the path to process the php files as the third argment (when using the '-t' flag).");
                    Console.WriteLine("Press [ENTER] to continue");
                    Console.ReadLine();
                    return;
                }

                new PhpTestHarness(args[1], args.Length > 2 ? args[2] : null);
                Console.WriteLine("Press [ENTER] to continue");
                Console.ReadLine();
            }
            else {
                string code = File.ReadAllText(args[0]);
                PhpGrammer grammar = new PhpGrammer();
                LanguageData language = new LanguageData(grammar);
                Parser parser = new Parser(language);
                parser.Parse(code);
                ParseTree parseTree = parser.Context.CurrentParseTree;
                if (parseTree.HasErrors()) {
                    foreach (var error in parseTree.ParserMessages) {
                        Console.WriteLine(error);
                    }
                }
                else {
                    ParseTreeNode program = parseTree.Root;
                    CompileTree(program).DynamicInvoke();
                }
            }
        }

        private static Delegate CompileTree(ParseTreeNode syntaxTree) {
            LambdaExpression Lambda = Expression.Lambda(CompileDocument(syntaxTree));
            return Lambda.Compile();
        }

        private static Expression CompileStatement(ParseTreeNode syntaxTree) {
            var current = syntaxTree.FirstChild;
            switch(current.Term.Name) {
                case "echoStatement":
                    var stringStatement = returnNode(current, "stringStatement");
                    var stringValue = CompileString(stringStatement);
                    return Echo(stringValue);
                    break;
            }
            return Expression.Empty();
        }

        private static string EscapeString(string stringValue) {
            stringValue = stringValue.Replace("\\n", "\n");
            stringValue = stringValue.Replace("\\t", "\t");
            return stringValue;
        }

        private static string CompileString(ParseTreeNode node) {
            switch(node.Term.Name) {
                case "stringStatement":
                    return CompileString(node.FirstChild);
                case "stringSegment":
                    return CompileString(node.FirstChild);
                case "stringRun":
                    var stringBuilder = new StringBuilder();
                    foreach (var runChild in node.ChildNodes) {
                        stringBuilder.Append(CompileString(runChild));
                    }
                    return stringBuilder.ToString();
                case "doubleQuotedString":
                    return node.ChildNodes.Count == 5 ? EscapeString(node.ChildNodes[2].Token.ValueString) : string.Empty;
                case "singleQuotedString":
                    return node.ChildNodes.Count == 5 ? node.ChildNodes[2].Token.ValueString : string.Empty;
            }
            return string.Empty;
        }

        public static Expression Echo(string value) {
            InvocationExpression invocationExpression = Expression.Invoke(echoExpression, Expression.Constant(value));
            return invocationExpression;
        }

        private static Expression CompileDocumentSegment(ParseTreeNode segment) {
            var php = returnNode(segment, "php");
            if (php != null) {
                var statements = returnNode(php, "statements");
                if (statements != null) {
                    return CompilePhp(statements);
                }
            }
            return null;
        }

        private static Expression CompilePhp(ParseTreeNode php) {
            IList<Expression> statements = new List<Expression>();
            if (php != null && php.FirstChild != null) {
                if (php.FirstChild.ChildNodes != null) {
                    foreach (var statement in php.ChildNodes) {
                        statements.Add(CompileStatement(statement));
                    }
                }

                //switch (binary.Op) {
                //    case "+":
                //        return Expression.Add(left, right);
                //    case "-":
                //        return Expression.Subtract(left, right);
                //    case "*":
                //        return Expression.Multiply(left, right);
                //    case "/":
                //        return Expression.Divide(left, right);
                //    case "**":
                //        return Expression.Power(Expression.Convert(left, typeof(double)),
                //                                Expression.Convert(right, typeof(double)));
                //    default:
                //        throw new NotSupportedException();
                //}
            }
            else {
                //Token token = (Token)syntaxTree.ChildNodes.First();
                //return Expression.Constant(token.Value);
            }
            return Expression.Block(statements);
        }

        private static ParseTreeNode returnNode(ParseTreeNode parent, string name) {
            foreach (var node in parent.ChildNodes) {
                if (node.Term.Name.CompareTo(name) == 0) {
                    return node;
                }
            }
            return null;
        }

        private static Expression<Action<string>> echoExpression;
        private static Expression CompileDocument(ParseTreeNode syntaxTree) {
            IList<Expression> expressions = new List<Expression>();
            echoExpression = x => Console.Write(x);
            // Creating a block expression that combines two method calls.

            foreach (var documentSegment in syntaxTree.ChildNodes) {
                var segment = CompileDocumentSegment(documentSegment);
                if(segment != null) {
                    expressions.Add(segment);
                }
            }

            
            return Expression.Block(expressions);
        }


    }
}
